﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace DiskInventory
{
    /// <summary>
    /// Maintain file folder descriptions
    /// </summary>
    public partial class DiskInventory : Form
    {
        /// <summary>
        /// The inventory as a dataset
        /// </summary>
        private DiskInventoryDataSet DiskInventoryDataSet = new DiskInventoryDataSet();
        /// <summary>
        /// Form that show help html
        /// </summary>
        private HelpForm Helper = new HelpForm();
        /// <summary>
        /// Maintain file folder descriptions
        /// </summary>
        public DiskInventory()
        {
            InitializeComponent();
            DiskInventoryDataSet.Load();
            Clean();
            InitializeTreeList();
        }
        /// <summary>
        /// Form loaded, populate tree
        /// </summary>
        /// <param name="e"></param>
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            if (!string.IsNullOrEmpty(Properties.Settings.Default.TreeViewState))
            {
                try
                {
                    this.treeListView.RestoreState(System.Convert.FromBase64String(
                        Properties.Settings.Default.TreeViewState));
                }
                catch
                {
                }
            }
        }
        /// <summary>
        /// Remove unused rows from dataset and save
        /// </summary>
        private void Clean()
        {
            bool Cleaned = false;
            foreach (DiskInventoryDataSet.DiskInventoryRow Row in this.DiskInventoryDataSet.DiskInventory)
            {
                if (!System.IO.Directory.Exists(Row.Path) || Row.IsDescriptionNull() || string.IsNullOrEmpty(Row.Description))
                {
                    Cleaned = true;
                    Row.Delete();
                }
            }
            if (Cleaned)
                SaveDataSet();
        }
        /// <summary>
        /// True if folder has sub folders
        /// </summary>
        /// <param name="aDir">Folder to check</param>
        /// <returns>True if folder has sub folders</returns>
        bool HasSubs(System.IO.DirectoryInfo aDir)
        {
            bool Result = false;
            try
            {
                Result = aDir.GetDirectories().Length > 0;
            }
            catch 
            {
            }
            return Result;
        }
        /// <summary>
        /// Set up the tree view
        /// </summary>
        private void InitializeTreeList()
        {
            // Tells tree view if this node can expand, here means contins subdirectory
            this.treeListView.CanExpandGetter = delegate(object x)
            {
                return (x is System.IO.DirectoryInfo) && HasSubs((System.IO.DirectoryInfo)x);
            };
            // Get children of a node, here we get files and subs
            this.treeListView.ChildrenGetter = delegate(object x)
            {
                System.IO.DirectoryInfo dir = (System.IO.DirectoryInfo)x;
                try
                {
                    return new ArrayList(dir.GetFileSystemInfos());
                }
                catch (UnauthorizedAccessException ex)
                {
                    MessageBox.Show(this, ex.Message, "ObjectListViewDemo", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                    return new ArrayList();
                }
            };

            // You can change the way the connection lines are drawn by changing the pen
            BrightIdeasSoftware.TreeListView.TreeRenderer renderer = this.treeListView.TreeColumnRenderer;
            renderer.LinePen = new Pen(Color.Firebrick, 0.5f);
            renderer.LinePen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;

            // Draw the system icon next to the name
            SysImageListHelper helper = new SysImageListHelper(this.treeListView);
            this.treeColumnName.ImageGetter = delegate(object x)
            {
                return helper.GetImageIndex(((System.IO.FileSystemInfo)x).FullName);
            };
            // Show only folders; Do show root folders, Don't show System folders
            this.treeListView.ModelFilter = new BrightIdeasSoftware.ModelFilter(delegate(object x)
            {
                return 
                    ((System.IO.FileSystemInfo)x).Name.EndsWith(@":\") ||
                    (((System.IO.FileSystemInfo)x).Attributes.HasFlag(System.IO.FileAttributes.Directory) &&
                    !((System.IO.FileSystemInfo)x).Attributes.HasFlag(System.IO.FileAttributes.System));
            });
            // 
            this.treeColumnDescription.FillsFreeSpace = true;
            this.treeListView.AddDecoration(new BrightIdeasSoftware.EditingCellBorderDecoration { UseLightbox = true });
            // Get the descriptions
            this.treeColumnDescription.AspectGetter = delegate(object x)
            {
                DiskInventoryDataSet.DiskInventoryRow Row = this.DiskInventoryDataSet.DiskInventory.FindByPath(
                    ((System.IO.FileSystemInfo)x).FullName);
                return Row == null ? string.Empty : Row.Description;
            };
            // Show the file attributes for this object
            this.treeColumnAttributes.AspectGetter = delegate(object x)
            {
                return ((System.IO.FileSystemInfo)x).Attributes;
            };
            BrightIdeasSoftware.FlagRenderer attributesRenderer = new BrightIdeasSoftware.FlagRenderer();
            attributesRenderer.Add(System.IO.FileAttributes.Archive, "archive");
            attributesRenderer.Add(System.IO.FileAttributes.ReadOnly, "readonly");
            attributesRenderer.Add(System.IO.FileAttributes.System, "system");
            attributesRenderer.Add(System.IO.FileAttributes.Hidden, "hidden");
            attributesRenderer.Add(System.IO.FileAttributes.Temporary, "temporary");
            this.treeColumnAttributes.Renderer = attributesRenderer;

            // List all drives as the roots of the tree
            ArrayList roots = new ArrayList();
            foreach (System.IO.DriveInfo di in System.IO.DriveInfo.GetDrives())
            {
                if (di.IsReady && di.DriveType == System.IO.DriveType.Fixed || 
                    di.DriveType == System.IO.DriveType.Network)
                {
                    roots.Add(new System.IO.DirectoryInfo(di.Name));
                }
            }
            this.treeListView.Roots = roots;
            this.treeListView.Expand(roots[0]);
        }
        /// <summary>
        /// Double clicked on folder - open in Explorer
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void treeListView_ItemActivate(object sender, EventArgs e)
        {
            ShellUtilities.Execute(((System.IO.FileSystemInfo)this.treeListView.SelectedObject).FullName);
        }
        /// <summary>
        /// Edited a cell
        /// </summary>
        private void treeListView_CellEditFinishing(object sender, BrightIdeasSoftware.CellEditEventArgs e)
        {
            // Get the folder name
            string Path = ((System.IO.FileSystemInfo)(e.RowObject)).FullName;
            // Find it in the dataset
            DiskInventoryDataSet.DiskInventoryRow Row = this.DiskInventoryDataSet.DiskInventory.FindByPath(Path);
            // If it user removed the description delete the row, if it was found
            if (string.IsNullOrEmpty((string)e.NewValue))
            {
                if (Row != null)
                {
                    Row.Delete();
                    SaveDataSet();
                }
            }
            else  // User entered a description Add or Mod th row
            {
                if (Row == null)
                {
                    this.DiskInventoryDataSet.DiskInventory.AddDiskInventoryRow(Path, (string)e.NewValue);
                }
                else
                {
                    Row.Description = (string)e.NewValue;
                }
                // Save the XML
                SaveDataSet();
            }
        }
        /// <summary>
        /// Clicked the Open in Excel button
        /// </summary>
        private void toolStripButtonOpenExcel_Click(object sender, EventArgs e)
        {
            // Nothing to show, let user know and return
            if (this.DiskInventoryDataSet.DiskInventory.Count == 0)
            {
                MessageBox.Show("No data entries (descriptions) to open.");
                return;
            }
            // Open er up
            try
            {
                OpenInExcel(DiskInventoryDataSet.DefaultPath());
            }
            catch (Exception Ex)
            {
                MessageBox.Show("Error opening excel: " + Ex.Message);
            }
        }
        /// <summary>
        /// Open file in Excel
        /// </summary>
        /// <param name="aXmlFile">File to open</param>
        private void OpenInExcel(string aXmlFile)
        {
            Type ExcelType = Type.GetTypeFromProgID("Excel.Application");
            if (ExcelType == null)
            {
                MessageBox.Show("Excel is not installed on this computer.");
                return;
            }
            SaveFileDialog sfd = new SaveFileDialog();
            sfd.Title = "Enter XL worksheet name:";
            sfd.Filter = "Excel worksheet (*.xls)|(*.xls)";
            sfd.FileName = "InventoryForm.xls";
            if (sfd.ShowDialog() == System.Windows.Forms.DialogResult.Cancel) return;

            // The Globalization stuff is to work around an Office 2003 bug. 
            System.IO.File.Copy(System.IO.Path.Combine(Application.StartupPath, "InventoryFormNov2011.xls"),
                sfd.FileName, true);
            // To make this work in all versions of excel reflection is used.
            object oMissing = System.Reflection.Missing.Value;
            System.Globalization.CultureInfo ci =
                new System.Globalization.CultureInfo("en-US");
            object oExcel = Activator.CreateInstance(ExcelType);
            oExcel.GetType().InvokeMember("Visible", System.Reflection.BindingFlags.SetProperty, null, oExcel,
                new object[] { false }, ci);
            object oBooks = oExcel.GetType().InvokeMember("Workbooks",
                System.Reflection.BindingFlags.GetProperty, null, oExcel, null, ci);
            object oBook = oBooks.GetType().InvokeMember("Open",
                System.Reflection.BindingFlags.InvokeMethod, null, oBooks,
                new object[] { sfd.FileName, oMissing, true }, ci);
            object oSheets = oBook.GetType().InvokeMember("Sheets", 
                System.Reflection.BindingFlags.GetProperty, null, oBook, null);
            object oSheet = oSheets.GetType().InvokeMember("Item", System.Reflection.BindingFlags.GetProperty,
                null, oSheets, new object[] { 1 });
            object oCells = oSheet.GetType().InvokeMember("Cells", System.Reflection.BindingFlags.GetProperty,
                null, oSheet, null);
            int RowNumber = 13;
            foreach (DiskInventoryDataSet.DiskInventoryRow Row in this.DiskInventoryDataSet.DiskInventory)
            {
                oCells.GetType().InvokeMember("Item", System.Reflection.BindingFlags.SetProperty,
                    null, oCells, new object[] { RowNumber, 2, Row.Path });
                oCells.GetType().InvokeMember("Item", System.Reflection.BindingFlags.SetProperty,
                    null, oCells, new object[] { RowNumber, 7, Row.Description });
                RowNumber++;
            }
            oExcel.GetType().InvokeMember("Visible", System.Reflection.BindingFlags.SetProperty, null, oExcel,
                new object[] { true }, ci);
            oBook.GetType().InvokeMember("UserControl", System.Reflection.BindingFlags.SetProperty, null, oExcel,
                new object[] { true });
        }
        /// <summary>
        /// Print selected, use the dialog
        /// </summary>
        private void printToolStripButton_Click(object sender, EventArgs e)
        {
            new PrintDialog().ShowDialog(this.treeListView);
        }
        /// <summary>
        /// Clear selected, warn user and clear dataset by deleting XML
        /// </summary>
        private void newToolStripButton_Click(object sender, EventArgs e)
        {
            if (MessageBox.Show("All descriptions will be removed; continue?", "NEW", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation) == System.Windows.Forms.DialogResult.No) return;
            try
            {
                if (System.IO.File.Exists(DiskInventoryDataSet.DefaultPath()))
                    System.IO.File.Delete(DiskInventoryDataSet.DefaultPath());
            }
            catch (Exception Ex)
            {
                MessageBox.Show("Error clearing database: " + Ex.Message);
            }
        }
        /// <summary>
        /// Save As clicked, get file and save XML
        /// </summary>
        private void saveToolStripButton_Click(object sender, EventArgs e)
        {
            SaveFileDialog sfd = new SaveFileDialog();
            sfd.Title = "Enter filename to save database.";
            sfd.Filter = "XML files (*.xml)|*.xml|Comma separated values files (*.csv)|*.csv";
            if (sfd.ShowDialog() == System.Windows.Forms.DialogResult.Cancel) return;
            try
            {
                this.DiskInventoryDataSet.Save(sfd.FileName);
            }
            catch (Exception Ex)
            {
                string Message = "Error saving database to " + sfd.FileName + ": " + Ex.Message;
                if (Ex.InnerException != null) Message += "\n" + Ex.InnerException.Message;
                MessageBox.Show(Message);
            }
        }
        /// <summary>
        /// Help pressed, show help html file
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void helpToolStripButton_Click(object sender, EventArgs e)
        {
            // If form was exited, make a new one...
            if (Helper.IsDisposed) Helper = new HelpForm();
            Helper.Show();
            Helper.BringToFront();
        }
        /// <summary>
        /// Show About box (left click in toolbar)
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void aboutDiskInventoryToolStripMenuItem_Click(object sender, EventArgs e)
        {
            new AboutBox().ShowDialog();
        }
        /// <summary>
        /// Exiting, save state
        /// </summary>
        private void DiskInventory_FormClosing(object sender, FormClosingEventArgs e)
        {
            Properties.Settings.Default.TreeViewState = System.Convert.ToBase64String(
                this.treeListView.SaveState());
            Properties.Settings.Default.Save();
        }
        /// <summary>
        /// A way to reset the state (from context menu in tool bar).
        /// </summary>
        private void resetToDefaultStateToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Properties.Settings.Default.TreeViewState = string.Empty;
            Properties.Settings.Default.Save();
            MessageBox.Show("DiskInventory will now exit.");
            this.Close();
        }
        /// <summary>
        /// Save the database, allow retry on error.
        /// </summary>
        private void SaveDataSet()
        {
            DialogResult Result;
            do
            {
                try
                {
                    this.DiskInventoryDataSet.Save();
                    Result = System.Windows.Forms.DialogResult.Cancel;
                }
                catch (Exception Ex)
                {
                    Result = MessageBox.Show("Error saving database: " + Ex.Message, "ERROR", 
                        MessageBoxButtons.RetryCancel, MessageBoxIcon.Error);
                }
            } while (Result == System.Windows.Forms.DialogResult.Retry);
        }
        private void openToolStripButton_Click(object sender, EventArgs e)
        {
            OpenFileDialog sfd = new OpenFileDialog();
            sfd.Title = "Enter filename to read.";
            sfd.Filter = "XML files (*.xml)|*.xml";
            if (sfd.ShowDialog() == System.Windows.Forms.DialogResult.Cancel) return;
            if (MessageBox.Show("All current data will be overwritten.  Continue?", "OVERWRITTEN", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation) == System.Windows.Forms.DialogResult.No) return;
            try
            {
                this.DiskInventoryDataSet.DiskInventory.Clear();
                this.DiskInventoryDataSet.Load(sfd.FileName);
                SaveDataSet();
            }
            catch (Exception Ex)
            {
                string Message = "Error opening database" + sfd.FileName + ": " + Ex.Message;
                if (Ex.InnerException != null) Message += "\n" + Ex.InnerException.Message;
                MessageBox.Show(Message);
            }
        }
    }
}
